package com.hysf.myAnnotation.org.myframework.context;

import com.hysf.myAnnotation.org.myframework.annotation.*;
import lombok.Getter;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/*
* 自定义IOC容器
* beanMap: 存储Bean实例
* beanDefinitionMap: 存储Bean定义 包括Bean的全限定名、Bean的作用域、Bean是否懒加载
* */
@Getter
public class MyAnnotationConfigApplicationContext implements MyApplicationContext {
	private static final Class<?>[] COMPONENT_ANNOTATIONS = {
			MyConfiguration.class,
			MyComponent.class,
			MyService.class,
			MyRepository.class
	};
	
	private final Map<String, Object> beanMap = new ConcurrentHashMap<>();
	private final Map<String, MyBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
	
	public MyAnnotationConfigApplicationContext(Class<?>... componentClasses) {
		try {
			for (Class<?> componentClass : componentClasses) {
				// 1. 如果自身为配置类则交给Spring管理
				parseBeanAnnotation(componentClass);
				// 2. 扫描包下的类
				if (componentClass.isAnnotationPresent(MyComponentScan.class)) {
					MyComponentScan componentScanAnnotation = componentClass.getAnnotation(MyComponentScan.class);
					String[] basePackages = componentScanAnnotation.basePackages();
					if (basePackages == null || basePackages.length == 0) {
						basePackages = componentScanAnnotation.value();
					}
					if (basePackages == null || basePackages.length == 0) {
						basePackages = new String[]{componentClass.getPackage().getName()};
					}
					recursiveLoadBeanDefinition(basePackages);
				}
				// 3.创建Bean实例
				createBeans();
				// 4. 依赖注入
				doDis();
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/*
	* 基础数据类型和String的注入
	* value: 要注入的值
	* field: 要注入的字段
	* */
	private Object getDiBaseType(String value, Field field) {
		Class<?> fieldType = field.getType(); // 获取到这个字段的类型
		field.setAccessible(true); // 设置当前对象类的字段访问权限
		if (fieldType == String.class){
			return value;
		} else {
			Object obj;
			// 将String转换为基础类型
			if (fieldType == int.class || fieldType == Integer.class) {
				value = value.replaceAll("\\s+", ""); // 去除字符串中的空格
				if (value.matches("^[+-]?\\d+$")) { // 判断是否为整数
					value = value.replaceAll("^[+-]?", ""); // 去除字符串中的符号
					obj = Integer.parseInt(value);
				} else {
					throw new RuntimeException("无法将字符串转换为整数: " + value);
				}
			} else if (fieldType == double.class || fieldType == Double.class) {
				value = value.replaceAll("\\s+", ""); // 去除字符串中的空格
				if (value.matches("^[+-]?\\d+(\\.\\d+)?$")) { // 判断是否为浮点数
					value = value.replaceAll("^[+-]?", ""); // 去除字符串中的符号
					obj = Double.parseDouble(value);
				} else {
					throw new RuntimeException("无法将字符串转换为浮点数: " + value);
				}
			} else if (fieldType == boolean.class || fieldType == Boolean.class) {
				value = value.replaceAll("\\s+", ""); // 去除字符串中的空格
				if (value.matches("^(true|false)$")) { // 判断是否为布尔值
					obj = Boolean.parseBoolean(value);
				} else {
					throw new RuntimeException("无法将字符串转换为布尔值: " + value);
				}
			} else {
				throw new RuntimeException("不支持的类型: " + fieldType.getName());
			}
			return obj;
		}
	}
	
	/*
	* 按照类型注入
	* fieldType: 字段的类型
	* */
	private Object getAccordingTypeToDi(Class<?> fieldType, boolean isRequired) {
		for (Map.Entry<String, Object> entry : beanMap.entrySet()) { // 遍历所有的Bean
			if (fieldType.isAssignableFrom(entry.getValue().getClass())) { // 如果这个Bean的类型和字段的类型相同
				return entry.getValue(); // 获取到这个Bean的实例
			}
		}
		if (!isRequired){ // 非强制依赖注入
			return null;
		}
		// 如果容器中不存在这个Bean则查看是否有Bean定义
		for (Map.Entry<String, MyBeanDefinition> entry : beanDefinitionMap.entrySet()) { // 遍历所有的Bean
			MyBeanDefinition myBeanDefinition = entry.getValue(); // 获取到这个Bean的定义
			String className = myBeanDefinition.getClassName(); // 获取到这个Bean的全限定名
			try {
				Class<?> clazz = Class.forName(className); // 获取到这个Bean的Class对象
				if (fieldType.isAssignableFrom(clazz)) { // 如果这个Bean的类型和字段的类型相同
					Object obj = clazz.newInstance(); // 创建这个Bean的实例
					// 依赖注入
					doDi(obj);
					if (myBeanDefinition.isSingle()){ // 如果这个Bean是单例的
						beanMap.put(entry.getKey(), obj); // 将这个Bean的实例放入容器中
					}
					return obj; // 返回这个Bean的实例
				}
			} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}
		return null;
	}
	
	/*
	* 对一个对象进行依赖注入
	* object: 要注入的对象
	* */
	private void doDi(Object object) throws IllegalAccessException {
		Class<?> clazz = object.getClass(); // 获取到这个对象的Class对象
		Field[] fields = clazz.getDeclaredFields(); // 获取到这个对象的所有字段
		for (Field field : fields) { // 遍历所有的字段
			Object obj = null;
			if (field.isAnnotationPresent(MyResource.class)) { // 先按照名称注入 再按照类型注入
				MyResource anno = field.getAnnotation(MyResource.class); // 获取注解
				String beanId = anno.value(); // 获取注解的值 bean的id 如果没有值 则按照类型注入
				if (beanId.isEmpty()) { // 按类型注入
					Class<?> fieldType = field.getType(); // 获取到这个字段的类型
					obj = getAccordingTypeToDi(fieldType,true); // 获取到需要依赖的Bean的实例
				} else { // 按名称注入
					obj = getBean(beanId); // 获取到需要依赖的Bean的实例
				}
			} else if(field.isAnnotationPresent(MyValue.class)){ // 基础数据类型和String
				MyValue anno = field.getAnnotation(MyValue.class); // 获取注解
				String value = anno.value(); // 获取注解的值
				obj = getDiBaseType(value, field);
			} else if(field.isAnnotationPresent(MyAutowired.class)) { // 先按照类型注入 再按照名称注入
				MyAutowired anno = field.getAnnotation(MyAutowired.class); // 获取注解
				boolean isRequired = anno.required(); // 获取注解的值
				Class<?> fieldType = field.getType(); // 获取到这个字段的类型
				obj = getAccordingTypeToDi(fieldType, isRequired);
				if (obj == null){
					obj = getBean(field.getName(),isRequired); // 获取到需要依赖的Bean的实例
				}
			}
			field.setAccessible(true); // 设置当前对象类的字段访问权限
			field.set(object, obj); // 将需要依赖的Bean的实例注入到当前对象的字段中
		}
	}
	
	/*
	* 对所有的Bean进行依赖注入
	* */
	private void doDis() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
		for (Map.Entry<String, Object> entry : beanMap.entrySet()) { // 给所有已经创建的Bean进行依赖注入
			// 遍历所有的Bean
			Object object = entry.getValue(); // 获取到这个对象的实例
			doDi(object);
		}
	}
	
	/*
	* 包扫描
	* PackagePath: 包路径
	* packageName: 包名 在获取Class对象时使用
	* */
	private void findPackageClasses(String PackagePath, String packageName) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException {
		if (PackagePath.startsWith("/")){ // 去除开头的/
			PackagePath = PackagePath.substring(1);
		}
		File file = new File(PackagePath); // 获取当前地址的文件对象
		// 获取子文件 并过滤掉无关文件
		File[] childFile = file.listFiles(pathname -> file.isDirectory() || file.getName().endsWith(".class"));
		if (childFile != null) {
			for (File f:childFile){
				if (f.isDirectory()){ // 如果是目录 则递归
					findPackageClasses(f.getAbsolutePath(), packageName + "." + f.getName());
				}else { // 如果是.class文件 则加载
					// 初始化类加载器
					URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{});
					// 获取类名
					String className = packageName + "." + f.getName().replace(".class", "");
					// 加载类的Class对象
					Class<?> componentClass = urlClassLoader.loadClass(className);
					if (componentClass.isAnnotationPresent(MyComponent.class) ||
							componentClass.isAnnotationPresent(MyService.class) ||
							componentClass.isAnnotationPresent(MyRepository.class) ||
							componentClass.isAnnotationPresent(MyConfiguration.class)) {
						MyBeanDefinition bd = createBeanDefinition(componentClass);
						bd.setClassName(packageName + "." + f.getName().replaceAll(".class", ""));
						String value = getBeanBaseName(componentClass);
						value = getBeanName(value);
						beanDefinitionMap.put(value, bd);
					}
				}
			}
		}
		
	}
	
	/*
	* 启动递归加载Bean定义
	* basePackages: 包名数组
	* */
	private void recursiveLoadBeanDefinition(String[] basePackages) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
		for (String packageName : basePackages) { // 遍历所有的包名
			String pPath = packageName.replaceAll("\\.", "/"); // 将包名中的.替换为/
			Enumeration<URL> files = Thread.currentThread().getContextClassLoader().getResources(pPath);
			while (files.hasMoreElements()) {
				URL url = files.nextElement();
				// 这个URL可能是 目录 也可能是 .class 文件 需要递归
				findPackageClasses(url.getPath(), packageName); // 真正用于递归的函数
			}
		}
	}
	
	/*
	* 如果没有约定名称 则按照首字母小写的方式命名
	* */
	private String getBeanName (String className){
		return className.toLowerCase().charAt(0) + className.substring(1);
	}
	
	/*
	* 如果当前Bean被组件类型的注解(@Component、@Configuration、@Service、@Repository、@Controller...)标注
	* 则获取Bean的名称
	* */
	private String getBeanBaseName(Class<?> componentClass) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
		for (Class<?> annotationType : COMPONENT_ANNOTATIONS) {
			if (componentClass.isAnnotationPresent((Class<? extends Annotation>) annotationType)) {
				Annotation annotation = componentClass.getAnnotation((Class<? extends Annotation>) annotationType);
				String res = (String) annotation.getClass().getMethod("value").invoke(annotation);
				if (Objects.equals(res, "")) {
					break;
				}
				return res;
			}
		}
		return getBeanName(componentClass.getSimpleName());
	}
	
	/*
	* 创建Bean实例
	* */
	private void createBeans() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		for (Map.Entry<String, MyBeanDefinition> entry : beanDefinitionMap.entrySet()) {
			String beanId = entry.getKey();
			MyBeanDefinition myBeanDefinition = entry.getValue();
			if (!myBeanDefinition.isLazy() && myBeanDefinition.isSingle()) {
				String classPathName = myBeanDefinition.getClassName();
				Class clazz = Class.forName(classPathName);
				Object o = clazz.newInstance();
				beanMap.put(beanId, o);
			}
		}
	}
	
	/*
	 * 创建Bean实例
	 * */
	private Object createBean(String beanId, MyBeanDefinition myBeanDefinition) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		String classPathName = myBeanDefinition.getClassName();
		Class clazz = Class.forName(classPathName);
		Object o = clazz.newInstance();
		return o;
	}
	
	/*
	 * 创建Bean定义
	 * */
	private MyBeanDefinition createBeanDefinition(Class<?> componentClass) {
		MyBeanDefinition bd = new MyBeanDefinition();
		if (componentClass.isAnnotationPresent(MyLazy.class)) { // 如果被@MyLazy标注
			bd.setLazy(componentClass.getAnnotation(MyLazy.class).value()); // 获取@MyLazy注解的value值
		}else{
			bd.setLazy(false); // 默认为非懒加载
		}
		if (componentClass.isAnnotationPresent(MyScope.class)){ // 如果被@MyScope标注
			bd.setSingle(Objects.equals(componentClass.getAnnotation(MyScope.class).value(), "singleton")); // 获取@MyScope注解的value值
		}else {
			bd.setSingle(true); // 默认为单例
		}
		bd.setClassName(componentClass.getName()); // 设置类的全限定名
		return bd;
	}
	
	/*
	* 用来加载配置类和Bean的方法
	* ApplicationContext: 应用上下文 参数列表中的Bean需要直接被创建
	* */
	private void parseBeanAnnotation(Class<?> componentClass) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		// 1. 自身需要被创建
		// 创建Bean实例 (参数列表中所有的Bean都要被创建)
		Object o = componentClass.newInstance();
		String value = getBeanName(getBeanBaseName(componentClass)); // 先获取Bean的名称(如果没有指定则按照首字母小写的方式命名)
		beanMap.put(value, o); // 将Bean放入BeanMap中
		
		// 创建Bean定义
		MyBeanDefinition bd = createBeanDefinition(componentClass);
		beanDefinitionMap.put(value, bd); // 将Bean定义放入BeanDefinitionMap中
		
		// todo 如果被@Comfiguration标注 则配置代理
		
		// 2. 创建类中的@MyBean方法标注的Bean
		for (Method method : componentClass.getDeclaredMethods()) {// 遍历所有的方法
			if (method.isAnnotationPresent(MyBean.class)) { // 如果方法被@MyBean标注
				MyBean myBean = method.getAnnotation(MyBean.class); // 获取@MyBean注解
				String beanId = myBean.value(); // 获取Bean的名称
				if (beanId == null || beanId.isEmpty()) { // 如果没有指定Bean的名称
					beanId = getBeanName(method.getName()); // 则按照方法名的方式命名
				}
				Object mo = method.invoke(o); // 调用方法获取Bean实例
				beanMap.put(beanId, mo); // 将Bean实例放入BeanMap中
				MyBeanDefinition methodBean = createBeanDefinition(method.getReturnType()); // 创建Bean定义
				beanDefinitionMap.put(beanId, methodBean); // 将Bean定义放入BeanDefinitionMap中
			}
		}
	}
	
	/*
	* 1.获取指定名称的Bean
	* 2.进行根据名称匹配的依赖注入
	* */
	@Override
	public Object getBean(String beanName) {
		return getBean(beanName,true);
	}
	
	@Override
	public Object getBean(String beanName, boolean isRequired) {
		if (beanMap.containsKey(beanName)){
			return beanMap.get(beanName);
		}
		if (!isRequired){
			return null;
		}
		if (!beanDefinitionMap.containsKey(beanName)){
			throw new RuntimeException("查无此Bean: " + beanName);
		}
		MyBeanDefinition myBeanDefinition = beanDefinitionMap.get(beanName);
		String className = myBeanDefinition.getClassName();
		try {
			Object obj = Class.forName(className).newInstance();
			// 依赖注入
			doDi(obj);
			if (myBeanDefinition.isSingle()){
				beanMap.put(beanName,obj);
			}
			return obj;
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	/*
	* 获取Bean定义的名称
	* */
	@Override
	public String[] getBeanDefinitionNames() {
		return beanDefinitionMap.keySet().toArray(new String[0]);
	}
}
